
#include "server.h"
#include "anet.h"

#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>

static char IP[] = "127.0.0.1";
static int PORT = 9999;

struct redisServer server;

/* Like serverLogRaw() but with printf-alike support. This is the function that
 * is used across the code. The raw version is only used in order to dump
 * the INFO output on crash. */
void _serverLog(int level, const char *fmt, ...) {
    va_list ap;
    char msg[LOG_MAX_LEN];

    va_start(ap, fmt);
    vsnprintf(msg, sizeof(msg), fmt, ap);
    va_end(ap);

    serverLogRaw(level,msg);
}

void initServer(void){
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    server.maxclients = 100;
    server.bindaddr = IP;
    server.port = PORT;
    server.tcp_backlog = 128;
    server.ipfd = -1;
    server.clients = listCreate();

    server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
    if (server.el == NULL) {
        serverLog(LL_WARNING,
            "Failed creating the event loop. Error message: '%s'",
            strerror(errno));
        exit(1);
    }

     /* Open the TCP listening socket for the user commands. */
    if (listenToPort(server.port,&server.ipfd) == C_ERR) {
        serverLog(LL_WARNING, "Failed listening on port %u (TCP), aborting.", server.port);
        exit(1);
    }

    if (createSocketAcceptHandler(server.ipfd, acceptTcpHandler) != C_OK) {
        serverLog(LL_WARNING, "nrecoverable error creating TCP socket accept handler.");
        exit(1);
    }
}

/* Create an event handler for accepting new connections in TCP or TLS domain sockets.
 * This works atomically for all socket fds */
int createSocketAcceptHandler(int sfd, aeFileProc *accept_handler) {
    if (aeCreateFileEvent(server.el, sfd, AE_READABLE, accept_handler,NULL) == AE_ERR) {
        /* Rollback */
        aeDeleteFileEvent(server.el, sfd, AE_READABLE);
        return C_ERR;
    }
    return C_OK;
}

int listenToPort(int port, int *fd) {
    int j;

    char* addr = server.bindaddr;
    
    /* Bind IPv4 address. */
    *fd = anetTcpServer(port,addr,server.tcp_backlog);
    
    if (*fd == ANET_ERR) {
        int net_errno = errno;
        serverLog(LL_WARNING,
            "Warning: Could not create server TCP listening socket %s:%d",
            addr, port);

        return C_ERR;
    }
    anetNonBlock(NULL,*fd);
    anetCloexec(*fd);
    
    return C_OK;
}

/* Low level logging. To use only for very big messages, otherwise
 * serverLog() is to prefer. */
void serverLogRaw(int level, const char *msg) {
    const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
    const char *c = ".-*#";
    FILE *fp;
    char buf[64];

    fp = stdout;
    if (!fp) return;
    fprintf(fp,"%s",msg);
    fflush(fp);
}

int main(int argc, char const *argv[])
{

    initServer();
    aeMain(server.el);
    aeDeleteEventLoop(server.el);
    return 0;
}
